/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

/**
 *
 * @author Merijn Zengers, Gerben Feenstra
 */
public class FileCopier {

    protected FileCopier(){}
    /**
     * Copy whole directory + content to the destination directory
     * 
     * @param source The source directory
     * @param destination The destination directory
     * @param extension The file-extension to search for in the sourcedir, for example ".shp". Null will copy all files from source dir to destination
     * @param bundleFiles In conjunction with 'extension': Copy files with same names but different extension also (like: If file is named thisisaname.shp, then copy thisisaname.shx, thisisaname.dbf etc. as well)
     * @throws IOException While creating a file
     * @throws NotADirectoryException The specified destination is not a directory
     * @throws NotAFileException The passed file (in the call to copyFile) is not a file 
     * @throws DirectoryCreationException When the destination directory cannot be created
     */
     public static void copyDirectory(File source, File destination, String extension, boolean bundleFiles) throws NotAFileException, NotADirectoryException, DirectoryCreationException, IOException {
        checkAndCreateDirectory(source);
        checkAndCreateDirectory(destination);
        
        // Not just copy the files from the source-location, but also the selected parent directory.
        File newDestination = new File(destination.getAbsolutePath() + source.getPath().substring(source.getPath().lastIndexOf(File.separator)));
        checkAndCreateDirectory(newDestination);
        
        // List files in source directory and initiate file copy
        for (File f : source.listFiles()) {
            if (f.isFile() && (extension == null || f.getName().toLowerCase().endsWith(extension))) {
                copyFile(f, newDestination, bundleFiles);
            }
        }
    }

    /**
     * Copies file 'source' to destination directory 'destination'
     * @param source
     * @param destination
     * @param bundleFiles Copy files with the same name but different extension also (like: If file is named thisisaname.shp, then copy thisisaname.shx, thisisaname.dbf etc. as well)
     * @throws NotADirectoryException
     * @throws NotAFileException
     * @throws DirectoryCreationException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void copyFile(File source, File destination, boolean bundleFiles) throws NotAFileException, NotADirectoryException, DirectoryCreationException, IOException {
        checkAndCreateFile(source);
        checkAndCreateDirectory(destination);

        if (bundleFiles) {
            // Find other files with the same name in the same path
            for (File f : source.getParentFile().listFiles()) {
                if (f.isFile()) {
                    String fileName = source.getName().substring(0, source.getName().lastIndexOf("."));
                    String bundleFileName = f.getName().substring(0, f.getName().lastIndexOf("."));
                    if (!fileName.equals("") && !bundleFileName.equals("")) {
                        if (fileName.equals(bundleFileName)) {
                            copyFile(f, destination);
                        }
                    }
                }
            }
        } else {
            copyFile(source, destination);
        }
    }

    /**
     * Copy sourcefile to destination directory using FileChannel
     * @param source
     * @param destination 
     */
     public static void copyFile(File source, File destination) throws NotAFileException, NotADirectoryException, DirectoryCreationException, FileNotFoundException, IOException {
        checkAndCreateFile(source);
        checkAndCreateDirectory(destination);

        // init both FileChannels
        FileChannel fileChannelSrc = null;
        FileChannel fileChannelDest = null;
            
        // Create new destination file in the destination directory
        StringBuilder sb = new StringBuilder(destination.getPath());
        sb.append(File.separator);
        sb.append(source.getName());
        File newFile = new File(sb.toString());

        // Get channels from both source and new destination file
        fileChannelSrc = new FileInputStream(source).getChannel();
        fileChannelDest = new FileOutputStream(newFile).getChannel();
        
        // Do the actual copy from the sourcefile to the new destination file
        fileChannelDest.transferFrom(fileChannelSrc, 0, fileChannelSrc.size());
        fileChannelDest.close();
        fileChannelSrc.close();
    }
   
    /**
     * Check if the specified File is not a directory. If it does not exist, create a new file.
     * @param file The File to check
     */
     private static void checkAndCreateFile(File file) throws NotAFileException, IOException {
        if (file.isDirectory()) {
            StringBuilder sb = new StringBuilder("The suplied path [");
            sb.append(file.getPath());
            sb.append("] is not a File, but a directory");
            throw new NotAFileException(sb.toString());
        }
        // Not a Directory, must be a file, so create it if it doesn't exist
        if (!file.exists()) {
            file.createNewFile();
        }
    }
    
    /**
     * Check if the specified File is not an actual file, but rather a directory. 
     * If this File-instance is indeed representing a directory this method will create the directory if it does not actually exist in the filesystem yet.
     * @param directory The directory file to check
     */
     private static void checkAndCreateDirectory(File directory) throws NotADirectoryException, DirectoryCreationException {
        if(directory.isFile()){
            StringBuilder sb = new StringBuilder("The suplied path [");
            sb.append(directory.getPath());
            sb.append("] is not a Directory");
            throw new NotADirectoryException(sb.toString());
        }    
        // Not a File, must be a directory, so create it
        if (!directory.exists()) {
            if (!directory.mkdirs()) {
                StringBuilder sb = new StringBuilder("Failed while creating directory: ");
                sb.append(directory.getPath());
                throw new DirectoryCreationException(sb.toString());
            }
        }

    }
    
     /*
     * Method to move a file.
     * The File wil be renamed to the desired name. It wil reside in the same directory the original file was in.
     * @param String originalPath: the path the file curently has
     * @Param Sting toDirectory: the desired directory to which the file must be moved
     * @param indicates if the method is permitted to overwrite any existing files with the same name in the proces of moving.
     *
     * ===============================
     *       NOT USED AS OF YET
     * 
     * TODO: ENABLE AND TEST IF NEEDED
     * ===============================
     * 
     *
    static public void moveFile(File source, File destination, boolean overwrite, boolean bundleFiles) throws NotAFileException, NotADirectoryException, DirectoryCreationException, FileMoveException {
        checkIsFile(source);
        checkIsFile(destination);
        if (bundleFiles) {
            for (File f : source.getParentFile().listFiles()) {
                if (f.isFile()) {
                    String shpFile = source.getName().substring(0, source.getName().indexOf("."));
                    String bundleFileName = f.getName().substring(0, f.getName().indexOf("."));
                    if (!shpFile.equals("") && !bundleFileName.equals("")) {
                        if (shpFile.equals(bundleFileName)) {
                            moveFile(f, destination, overwrite);
                        }
                    }
                }
            }
        } else {
            moveFile(source, destination, overwrite);
        }
    }

    static private void moveFile(File source, File destination, boolean overwrite) throws DirectoryCreationException, NotAFileException, FileMoveException, NotADirectoryException {
        checkIsFile(source);
        checkIsFile(destination);
        
        try {
            createDir(destination);
            StringBuilder sb = new StringBuilder(destination.getPath());
            sb.append(File.separator);
            sb.append(source.getName());
            renameFile(source, new File(sb.toString()), overwrite);
        } catch (FileRenameException ex) {
            StringBuilder sb2 = new StringBuilder("Unable to move file: ");
            sb2.append(source.getPath());
            throw new FileMoveException(sb2.toString(), ex);
        }
    }

    /*
     * Method to rename a file.
     * The File wil be renamed to the desired name. It wil rside in the same directory the original file was in.
     * @param String originalPath: the path the file curently has
     * @Param Sting desiredPath: the desired path for the file
     * @param indicates if the method is permitted to overwrite any existing files with the same name in the proces of renaming.
     * @return the path to the renamed file
     *
    static public void renameFile(File source, File destination, boolean overwrite) throws NotAFileException, FileRenameException {
        checkIsFile(source);
        checkIsFile(destination);
            if (destination.exists()) {
                if (overwrite) {
                    destination.delete();
                }
            }
            //Abble to rename?
            if (!source.renameTo(destination)) {
                StringBuilder sb2 = new StringBuilder("Unable to move file: ");
                sb2.append(source.getPath());
                throw new FileRenameException(sb2.toString());
            }
    }
*/
}
